// Object.defineProperty 对js 基本数据类型的双向绑定原理

class Vue {
	constructor(props) {
		this.$options = props
		this.$el = document.querySelector(props.el)
		this.$data = props.data
		this.$watchEvent = {}
		this.obServe(this.$data)
		this.ergodicData(this.$el)
		this.proxyDataFun(this.$data)
		
	}
	proxyDataFun(data) {
		if (!data || typeof data !== 'object') return
		for (let key in data) {
			Object.defineProperty(this, key, {
				get() {
					return data[key]
				},
				set(val) {
					data[key] = val
				}
			})
		}

	}

	obServe(data) {
		for (let key in data) {
			let value = data[key]
			const that = this
			const dep = new Dep()
			if (typeof value !== 'object') {
				Object.defineProperty(data, key, {
					get() {
						Dep.target && dep.addWatcher(Dep.target)
						return value
					},
					set(val) {
						value = val
						dep.notify()
					}
				})
			} else {
				this.obServe(data[key])
			}

		}
	}
	ergodicData(node) {
		node.childNodes.forEach((item, index) => {
			if (item.nodeType === 1) {
				if (item.hasAttribute('@click')) {
					const vkey = item.getAttribute('@click').trim()
					item.addEventListener('click', () => {
						this.$options.methods[vkey].bind(this)()
					})
				}
				if (item.hasAttribute('v-model')) {
					const vkey = item.getAttribute('v-model').trim()
					const value = vkey.split('.').reduce((newValue,k) => newValue[k],this.$data)
					item.value = value
					
					new Watch(this.$data,vkey,(value) => {
						item.value = value
					})
					item.addEventListener('input', (event) => {
						
						const newArr = vkey.split('.').slice(0,vkey.split('.').length - 1).reduce((newValue,k) => newValue[k],this.$data)
						const index = vkey.split('.')[vkey.split('.').length - 1]
						newArr[index] = item.value
						// vkey.split('.').reduce((newValue,k) => newValue[k],this.$data) = 
						
					})
				}
				const reg = /\{\{(.*?)\}\}/g;
				const a = item.textContent
				item.textContent = a.replace(reg, (a, b) => {
					const newValue = b.split('.').reduce((val, k) => val[k] ,this.$data)
					new Watch(this.$data,b,(newValue) => {
						item.textContent = newValue
					})
					return newValue
				})
			}
		})
	}

}

class Watch {
	constructor(vm, vKey, cd) {
		this.vm = vm
		this.vKey = vKey
		this.cd = cd
		Dep.target = this
		
		vKey.split('.').reduce((Value ,k) => Value[k],this.vm)
		
		Dep.target = null
		
	}
	update() {
		const newValue = this.vKey.split('.').reduce((newValue ,k) => newValue[k],this.vm)
		this.cd(newValue)
	}
}

class Dep {
	constructor() {
	   this.subs = [] 
	}
	
	addWatcher(watcher) {
		this.subs.push(watcher)
	}
	notify() {
		this.subs.forEach(v => {
			v.update()
		})
	}
}
